package cn.zhage.aimin.canal.client.autoconfigure;

import cn.zhage.aimin.canal.client.config.CanalProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import java.util.Optional;

/**
 * Canal任务调度器自动配置类
 * 
 * 功能概述：
 * 1. 为Canal客户端提供专用的任务调度器配置
 * 2. 支持虚拟线程特性，提升并发性能
 * 3. 根据配置属性动态调整调度器参数
 * 4. 集成Spring Boot自动配置机制
 * 
 * 设计特点：
 * - 使用Spring Boot自动配置模式，开箱即用
 * - 支持Java 19+的虚拟线程（Project Loom）
 * - 提供合理的默认配置，同时支持灵活定制
 * - 遵循Spring Boot配置优先级和条件装配原则
 * 
 * 虚拟线程优势：
 * - 轻量级：创建成本极低，可创建数百万个虚拟线程
 * - 高并发：非常适合IO密集型任务（如Canal数据处理）
 * - 简化编程：保持同步编程模型，无需复杂的异步回调
 * - 资源优化：减少载体线程数量，降低系统资源占用
 * 
 * 适用场景：
 * - Canal数据同步的定时轮询
 * - 大量并发的数据库变更事件处理
 * - 需要高并发但线程阻塞时间较长的任务
 * 
 * 配置示例：
 * <pre>
 * canal:
 *   scheduler:
 *     enabled: true                    # 启用调度器自动配置
 *     virtual-threads: true            # 启用虚拟线程
 *     pool-size: 10                    # 载体线程池大小
 *     thread-name-prefix: "canal-"     # 线程名称前缀
 *     thread-group-name: "canal-group" # 线程组名称
 *     wait-for-tasks-to-complete-on-shutdown: true  # 关闭时等待任务完成
 *     await-termination-seconds: 30    # 等待终止的最大时间
 * </pre>
 * 
 * @author 渣哥
 * @since 1.0.0
 */
@Slf4j
// @AutoConfiguration: Spring Boot 2.7+的现代自动配置注解，替代@Configuration
// 自动被Spring Boot的自动配置机制发现和加载
@AutoConfiguration
// @EnableScheduling: 启用Spring的调度功能，支持@Scheduled注解
// 这是使用定时任务的前提条件
@EnableScheduling
// @EnableConfigurationProperties: 启用配置属性绑定
// 将canal.scheduler前缀的配置自动绑定到CanalProperties类
@EnableConfigurationProperties(CanalProperties.class)
// @ConditionalOnProperty: 条件装配注解，只有当指定属性满足条件时才激活此配置
// value: 指定要检查的配置属性路径
// havingValue: 当属性值为"true"时激活配置
// matchIfMissing: 当属性不存在时是否激活（默认激活）
@ConditionalOnProperty(value = "canal.scheduler.enabled", havingValue = "true", matchIfMissing = true)
public class CanalSchedulerAutoConfiguration {

    /**
     * 创建Canal专用任务调度器Bean
     * 
     * 调度器特性：
     * - 支持虚拟线程：充分利用Java 19+的Project Loom特性
     * - 动态池大小：根据CPU核心数自动调整，也可手动配置
     * - 优雅关闭：支持应用关闭时等待任务完成
     * - 线程标识：便于监控和调试的线程命名
     * 
     * 虚拟线程工作原理：
     * 1. 虚拟线程运行在少数几个载体线程（carrier thread）上
     * 2. 当虚拟线程阻塞时，载体线程可以处理其他虚拟线程
     * 3. 极大提高了IO密集型任务的并发能力
     * 4. 对于Canal这种需要频繁等待网络IO的场景非常适合
     * 
     * 性能优势：
     * - 传统线程池：创建50个线程处理50个并发任务
     * - 虚拟线程：用5个载体线程处理5000个虚拟线程任务
     * - 内存占用：虚拟线程仅占用KB级别内存，传统线程需要MB级
     * 
     * @param canalProperties Canal配置属性，包含调度器相关配置
     * @return 配置完成的TaskScheduler实例
     */
    @Bean(name = "canalTaskScheduler")
    // @Primary: 标记为主要Bean，当存在多个同类型Bean时优先使用此Bean
    // 确保Canal调度器作为默认的TaskScheduler被注入
    @Primary
    // @ConditionalOnMissingBean: 条件装配，只有当容器中不存在TaskScheduler类型的Bean时才创建
    // 避免与其他组件的TaskScheduler冲突，遵循"不覆盖用户自定义配置"原则
    @ConditionalOnMissingBean(TaskScheduler.class)
    public TaskScheduler canalTaskScheduler(CanalProperties canalProperties) {

        log.info("正在创建Canal专用虚拟线程调度器...");
        
        // 获取调度器配置部分
        // 这些配置来自application.yml中的canal.scheduler.*属性
        CanalProperties.Scheduler config = canalProperties.getScheduler();
        
        // 创建Spring的线程池任务调度器
        // ThreadPoolTaskScheduler是Spring对ScheduledThreadPoolExecutor的封装
        // 提供了更好的Spring集成和配置能力
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();

        // 配置虚拟线程支持（Java 19+ Project Loom特性）
        // 虚拟线程优势：
        // 1. 轻量级：每个虚拟线程只占用几KB内存
        // 2. 海量并发：可以轻松创建百万级虚拟线程
        // 3. 简化编程：保持传统同步编程模型
        // 4. 自动调度：JVM自动管理虚拟线程在载体线程间的调度
        taskScheduler.setVirtualThreads(config.isVirtualThreads());
        
        // 设置线程名称前缀
        // 便于在日志、监控工具中识别Canal相关线程
        // 格式示例：canal-scheduler-1, canal-scheduler-2, ...
        taskScheduler.setThreadNamePrefix(config.getThreadNamePrefix());

        // 配置载体线程池大小
        // 对于虚拟线程：这是底层载体线程的数量，通常设置为CPU核心数
        // 对于传统线程：这是实际工作线程的数量
        // 使用Optional提供默认值：如果未配置则使用CPU核心数
        Integer poolSize = Optional.ofNullable(config.getPoolSize())
                .orElse(Runtime.getRuntime().availableProcessors()); // 获取可用CPU核心数作为默认值
        taskScheduler.setPoolSize(poolSize);

        // 配置优雅关闭行为
        // true: 应用关闭时等待正在执行的任务完成后再关闭调度器
        // false: 应用关闭时立即强制终止所有任务
        // 对于数据同步场景，建议设置为true以避免数据丢失
        taskScheduler.setWaitForTasksToCompleteOnShutdown(config.isWaitForTasksToCompleteOnShutdown());
        
        // 设置等待任务完成的最大时间（秒）
        // 如果超过此时间任务仍未完成，则强制关闭
        // 防止应用关闭时无限等待
        taskScheduler.setAwaitTerminationSeconds(config.getAwaitTerminationSeconds());
        
        // 设置线程组名称
        // 用于线程管理和监控，所有调度器线程都属于此线程组
        taskScheduler.setThreadGroupName(config.getThreadGroupName());

        // 记录调度器创建完成信息
        // 包含关键配置参数，便于运维监控
        log.info("Canal虚拟线程调度器创建完成 - 虚拟线程: {}, 载体线程数: {}, 线程前缀: {}", 
                config.isVirtualThreads(), poolSize, config.getThreadNamePrefix());
        
        return taskScheduler;
    }
    
    /**
     * 调度器配置最佳实践说明：
     * 
     * 1. 虚拟线程配置原则：
     *    - IO密集型任务：启用虚拟线程，设置较少的载体线程（CPU核心数）
     *    - CPU密集型任务：使用传统线程，线程数等于CPU核心数
     *    - 混合型任务：根据IO阻塞时间比例调整
     * 
     * 2. 载体线程数设置指导：
     *    - 虚拟线程模式：通常设置为CPU核心数
     *    - 传统线程模式：根据任务类型调整（IO密集型可适当增加）
     *    - 监控调整：观察CPU使用率和任务响应时间
     * 
     * 3. 关闭策略配置：
     *    - 数据同步场景：建议启用等待任务完成
     *    - 实时性要求高：可以选择立即关闭
     *    - 设置合理的等待时间避免无限等待
     * 
     * 4. 监控指标关注：
     *    - 活跃虚拟线程数
     *    - 载体线程利用率
     *    - 任务队列长度
     *    - 任务执行时间分布
     * 
     * 5. 性能调优建议：
     *    - 定期监控线程池状态
     *    - 根据业务峰值调整池大小
     *    - 考虑任务的IO阻塞特性
     *    - 测试不同配置下的性能表现
     */
}
